#include "Utils.h"
#include "Config.h"
#include "Scanner.h"
#include "ClamAVScanner.h"
#include <tclap/CmdLine.h>
#include <iostream>
#include <string>
#include <exception>
#include <ctime>

void main(int argc, char* argv[])
{
	bool cpuOnly;
	bool scanDrives;
	bool scanRemovableDrives;
	bool scanFolderRecursively;
	std::string folderPath;
	std::string configPath;

	if (!GPUCC::ClamAVScanner::Init())
	{
		std::cerr << "Unable to initialize ClamAV." << std::endl;
		return;
	}

	try
	{
		TCLAP::CmdLine cmd("GPUCC CLI Scanner!", ' ', "0.1");

		TCLAP::ValueArg<std::string> configPathArg("c", "config", "path to configuration file", false, ".\\config.json", "relative or full path", cmd);

		TCLAP::SwitchArg cpuOnlyArg("n", "nogpu", "scan using cpu only", false);

		TCLAP::SwitchArg scanDrivesArg("d", "drives", "scan drives mode", false);
		TCLAP::SwitchArg scanFolderArg("f", "folder", "scan folder", true);
		TCLAP::SwitchArg scanRemovableDrivesArg("r", "removable", "scan removalbe drives");
		TCLAP::ValueArg<std::string> folderPathArg("p", "path", "path to folder", false, "", "relative or full path");
		TCLAP::SwitchArg scanFolderRecursiveArg("l", "loop", "scan folder recursively", false);
		
		cmd.add(cpuOnlyArg);
		cmd.xorAdd(scanDrivesArg, scanFolderArg);
		cmd.add(scanRemovableDrivesArg);
		cmd.add(folderPathArg);
		cmd.add(scanFolderRecursiveArg);

		cmd.parse(argc, argv);

		cpuOnly = cpuOnlyArg.getValue();

		scanDrives = scanDrivesArg.getValue();
		configPath = GPUCC::Utils::GetFullPath(configPathArg.getValue());

		if (!scanFolderArg.isSet())
		{
			if (scanFolderRecursiveArg.isSet() || folderPathArg.isSet())
			{
				std::cerr << "Parameters: [-c config.json] -d [-r] | -f [-l] -p folder" << std::endl;
				return;
			}

			scanRemovableDrives = scanRemovableDrivesArg.getValue();
		}
		else
		{
			if (scanRemovableDrivesArg.isSet())
			{
				std::cerr << "Parameters: [-c config.json] -d [-r] | -f [-l] -p folder" << std::endl;
				return;
			}
			else if (!folderPathArg.isSet())
			{
				std::cerr << "Parameters: [-c config.json] -d [-r] | -f [-l] -p folder" << std::endl;
				return;
			}

			scanDrives = scanFolderArg.getValue();
			scanFolderRecursively = scanFolderRecursiveArg.getValue();
			folderPath = GPUCC::Utils::GetFullPath(folderPathArg.getValue());
		}
	}
	catch (TCLAP::ArgException &e)
	{
		std::cerr << "Error: " << e.error() << " for argument " << e.argId() << std::endl;

		return;
	}

	GPUCC::Config config;

	if (!GPUCC::Utils::FileExists(configPath))
	{
		std::cerr << "Configuration file doesn't exists!" << std::endl
				<< "Path: " << configPath << std::endl;
		return;
	}

	try
	{
		config.Load(configPath);
	}
	catch (std::exception& ex)
	{
		std::cerr << "Error loading configuration." << std::endl
				<< "Description: " << ex.what() << std::endl;
		return;
	}

	if ((!scanDrives) && (!GPUCC::Utils::DirectoryExists(folderPath)))
	{
		std::cerr << "Target folder doesn't exists!" << std::endl << folderPath << std::endl;
		return;
	}

	GPUCC::Scanner scanner(config, cpuOnly);

	if (!scanner.Init())
	{
		std::cerr << "Unable to initialize scanner!" << std::endl << std::endl;
		return;
	}
	else
	{
		std::cout << "Starting scan..."<< std::endl;

		std::time_t Start = std::time(NULL);
		std::localtime(&Start);
		
		std::vector<GPUCC::InfectedDetails> Results;

		if (scanDrives)
		{
			Results = scanner.ScanDrives(scanRemovableDrives);
		}
		else
		{
			Results = scanner.ScanFolder(folderPath, scanFolderRecursively);
		}

		std::time_t End = std::time(NULL);
		std::localtime(&End);

		std::cout << "Scan completed: ";
			
		if (Results.size() == 0)
		{
			std::cout << "Clean!" << std::endl;
		}
		else
		{
			std::cout << Results.size() << " infected files found!" << std::endl;

		}
		
		double TotalElapsedSeconds = std::difftime(End, Start);

		unsigned int ElapsedHours;
		unsigned int ElapsedMinutes;
		unsigned int ElapsedSeconds;

		GPUCC::Utils::ConvertFromTotalSeconds(TotalElapsedSeconds, ElapsedHours, ElapsedMinutes, ElapsedSeconds);

		std::cout << "- Total files scanned: " << scanner.GetScannedFilesCount() << "." << std::endl;
		std::cout << "- Finished computation with total time of " << ElapsedHours << ":" << ElapsedMinutes << ":" << ElapsedSeconds << "." << std::endl;

		if (Results.size() > 0)
		{
			for (std::vector<GPUCC::InfectedDetails>::iterator CurrentResult = Results.begin(); CurrentResult != Results.end(); ++CurrentResult)
			{
				std::cout << "* " << CurrentResult->GetPath() << "... " << CurrentResult->GetSignature() << std::endl;
			}
		}

		scanner.Cleanup();
	}
}